﻿using System;
using System.Collections;
using System.Linq;
using System.Linq.Expressions;


using SubSonic.DataProviders;
using SubSonic.Linq.Structure;

namespace Catalyst.Linq
{
	class CacheQueryContext
	{
		private static Expression getPrimaryTableExpression(MethodCallExpression mce)
		{
			foreach (Expression e in mce.Arguments)
			{
				MethodCallExpression m = e as MethodCallExpression;

				if (m != null)
				{
					return getPrimaryTableExpression(m);
				}

				return e;
			}

			return mce.Arguments[0];
		}

		// Executes the expression tree that is passed to it.
		internal static object Execute(Expression expression, bool IsEnumerable, IDataProvider provider, Type elementType)
		{
			// The expression must represent a query over the data source.
			if (!IsQueryOverDataSource(expression))
			{
				throw new InvalidProgramException("No query over the data source was specified.");
			}

			// Find the call to Where() and get the lambda expression predicate.
			InnermostWhereFinder whereFinder = new InnermostWhereFinder();
			MethodCallExpression whereExpression = whereFinder.GetInnermostWhere(expression);
			LambdaExpression lambdaExpression = (LambdaExpression)((UnaryExpression)(whereExpression.Arguments[1])).Operand;

			// Send the lambda expression through the partial evaluator.
			lambdaExpression = (LambdaExpression)Evaluator.PartialEval(lambdaExpression);

			// Get the place name(s) to query the Web service with.
			//LocationFinder lf = new LocationFinder(lambdaExpression.Body);
			//List<string> locations = lf.Locations;
			//if (locations.Count == 0)
			//    throw new InvalidQueryException("You must specify at least one place name in your query.");

			// Call CachingService and get the results.

			DbQueryProvider dbQueryProvider = new DbQueryProvider(provider);

			MethodCallExpression mce = expression as MethodCallExpression;

			Expression _expression = getPrimaryTableExpression(mce);

			object obj = dbQueryProvider.Execute(_expression);

			IEnumerable arr = (IEnumerable)obj;

			IEnumerable activeRecords = arr;// CachingService.GetCachedObject<List<IActiveRecord>>(getCacheKey(), new CachingService.ObjectLoader(getAllCachedData));

			// Copy the IEnumerable places to an IQueryable.
			IQueryable queryableActiveRecords = activeRecords.AsQueryable();

			// Copy the expression tree that was passed in, changing only the first
			// argument of the innermost MethodCallExpression.
			ExpressionTreeModifier treeCopier = new ExpressionTreeModifier(queryableActiveRecords, elementType);
			Expression newExpressionTree = treeCopier.CopyAndModify(expression);

			// This step creates an IQueryable that executes by replacing Queryable methods with Enumerable methods.
			if (IsEnumerable)
				return queryableActiveRecords.Provider.CreateQuery(newExpressionTree);
			else
				return queryableActiveRecords.Provider.Execute(newExpressionTree);
		}

		internal static bool IsQueryOverDataSource(Expression expression)
		{
			// If expression represents an unqueried IQueryable data source instance,
			// expression is of type ConstantExpression, not MethodCallExpression.
			return (expression is MethodCallExpression);
		}
	}
}
